
/**
 *
 * @author Jah Emanuel
 */
import java.io.FileInputStream;
import java.io.IOException;
import java.util.LinkedList;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 *
 * @author JE
 */
public class ReaderUtil {

    private static final String REGEX_TYPE = "\\.TYPE [N|D]";
    private static final String REGEX_GRAPH_H = "\\.GRAPH";
    private static final String REGEX_RESTRICTIONS_H = "\\.REST";
    private static final String REGEX_RESTRICTION = "((H:[A-Z0-9]{1,3}:)|(S:))(==|!=|>=|>>)[0-9]+";
    private static final String REGEX_CONNECTION = "[A-Z0-9]{1,3}:([A-Z0-9]{1,3},?)+";

    public static Graph getGraphFromString(String string) throws InvalidStringFormat, Exception {
        boolean isDirected = false;
        LinkedList<String> connections = new LinkedList<String>();
        LinkedList<String> restrictions = new LinkedList<String>();
        String text;
        Scanner scanner = null;
        boolean isGraphSt = false;
        boolean isRestSt = false;
        int lineNum = 0;

        Matcher typeMatcher;
        Matcher graphHeaderMatcher;
        Matcher restHeaderMatcher;
        Matcher connStMatcher;
        Matcher restStMatcher;
        Pattern typeHeaderPatt = Pattern.compile(REGEX_TYPE);
        Pattern graphHeaderPatt = Pattern.compile(REGEX_GRAPH_H);
        Pattern restHeaderPatt = Pattern.compile(REGEX_RESTRICTIONS_H);
        Pattern restStPatt = Pattern.compile(REGEX_RESTRICTION);
        Pattern connStPatt = Pattern.compile(REGEX_CONNECTION);

        try {
            scanner = new Scanner(string);

            //Se obtiene la primera linea
            if (scanner.hasNextLine()) {
                text = scanner.nextLine();
                typeMatcher = typeHeaderPatt.matcher(text);
                
                // Se verifica que la primera linea contenga el tipo de grafo
                if (typeMatcher.matches()) {
                    if (text.charAt(text.length() - 1) == 'D') {
                        isDirected = true;
                    } else if (text.charAt(text.length() - 1) == 'N') {
                        isDirected = false;
                    } else {
                        
                        throw new InvalidStringFormat("The type must be either D or N: " + String.format("%04d",lineNum));
                    }
                } else {
                    throw new InvalidStringFormat("The type is specified incorrectly: " + String.format("%04d",lineNum));
                }
            } else {
                throw new InvalidStringFormat("The string is empty: " + String.format("%04d",lineNum));
            }
            lineNum++;
            
            boolean tempGraphSt=false, tempRestSt=false;
            // Se itera sobre todas las líneas de la cadena
            while (scanner.hasNextLine()) {
                text = scanner.nextLine();
                lineNum++;
                
                graphHeaderMatcher = graphHeaderPatt.matcher(text);
                // Se valida que la cadena tenga formato de encabezado
                // de grafo
                if (graphHeaderMatcher.matches()) {
                    isGraphSt=false;
                    isRestSt=false;
                    tempGraphSt = true;
                    tempRestSt = false;
                } else {

                    restHeaderMatcher = restHeaderPatt.matcher(text);
                    // Se valida que la cadena tenga formato de encabezado
                    // de restricciones
                    if (restHeaderMatcher.matches()) {
                        isGraphSt=false;
                        isRestSt=false;
                        tempGraphSt = false;
                        tempRestSt = true;

                    } else {
                        if(!isGraphSt && !isRestSt)
                            // Si todo lo anterior falla y no están habilitados
                            // los modos, el formato no es correcto
                            throw new InvalidStringFormat("Incorrect format in line: " + String.format("%04d",lineNum));
                    }
                }
                    
                    
                // Está habilitado el modo lectura de conexiones de grafo?
                if (isGraphSt) {
                    connStMatcher = connStPatt.matcher(text);
                    // Se valida que la cadena tenga el formato correcto de
                    // conexion
                    if (connStMatcher.matches()) {
                        connections.add(text);
                    } else {
                        throw new InvalidStringFormat("Incorrect format in line: " + String.format("%04d",lineNum));
                    }
                } // Esta habilitado el modo lectura de restricciones?
                else if (isRestSt) {
                    restStMatcher = restStPatt.matcher(text);
                    // Se valida que la cadena tenga el formato correcto de 
                    // restriccion
                    if (restStMatcher.matches()) {
                        restrictions.add(text);
                    } else {
                        throw new InvalidStringFormat("Incorrect format in line: " + String.format("%04d",lineNum));
                    }
                }
                isGraphSt=tempGraphSt;
                isRestSt=tempRestSt;
            }
            if(connections.isEmpty()){
                throw new InvalidStringFormat("There are no defined connections");
            }
        } finally {
            if (scanner != null) {
                scanner.close();
            }
        }

        return returnGraphFromLists(isDirected, connections, restrictions);
    }
    
    private static Graph returnGraphFromLists(boolean isDirected, LinkedList<String> connections,LinkedList<String> restrictions) throws InvalidStringFormat, Exception{
        String[] concArrays;
        String[] firstSplit;
        String[] secondSplit;
        LinkedList<String[]> connect=new LinkedList<String[]>();
        LinkedList<String[]> restrict=new LinkedList<String[]>();
        for (String string : connections) {
            firstSplit=string.split(":");
            secondSplit=firstSplit[1].split(",");
            if(isDirected && secondSplit.length>1){
                throw new InvalidStringFormat("Invalid connection for directed graph: "+string);
            }
            concArrays=new String[1+secondSplit.length];
            System.arraycopy(firstSplit, 0, concArrays, 0, 1);
            System.arraycopy(secondSplit, 0, concArrays, 1, secondSplit.length);
            connect.add(concArrays);
        }
        for (String string : restrictions) {
            firstSplit=string.split(":");
            restrict.add(firstSplit);
        }
        return new Graph(isDirected, connect,restrict);
    }

    public static String getStringFromFile(String fileName) {
        StringBuilder sb = new StringBuilder();
        Scanner scanner = null;

        try {
            scanner = new Scanner(new FileInputStream(fileName));
            while (scanner.hasNextLine()) {
                sb.append(scanner.nextLine() + "\n");
            }
        } catch (IOException e) {
            System.out.println(e);
        } finally {
            if (scanner != null) {
                scanner.close();
            }
        }

        return sb.toString();
    }
}

